package com.application.service.Impl;


import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.application.controller.Result;
import com.application.dao.ActivityDao;
import com.application.domain.Activity;
import com.application.domain.Community;
import com.application.domain.RedisData;
import com.application.service.ActivityService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.application.util.RedisConstant.ACTIVITY_KEY;
import static com.application.util.RedisConstant.LOCK_ACTIVITY_KEY;

@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityDao,Activity> implements ActivityService {

    @Autowired
    private ActivityDao activityDao;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    @Transactional
    public int saveActivity(Activity activity) {
        int insert = activityDao.insert(activity);
        return insert;
    }

    @Override
    @Transactional
    public int deleteById(String id) {
        // 先删除数据库
        int deleteById = activityDao.deleteById(id);

        // 然后删除redis
        stringRedisTemplate.delete(ACTIVITY_KEY + id);
        return deleteById;
    }

    @Override
    @Transactional
    public int deleteByIds(List<String> ids) {
        int deleteBatchIds = activityDao.deleteBatchIds(ids);
        return deleteBatchIds;
    }

    @Override
    @Transactional
    public int deleteAll() {
        int delete = activityDao.delete(null);
        return delete;
    }

    @Override
    @Transactional
    public int updateByIdActivity(Activity activity) {
        // 先更新数据库
        int updateById = activityDao.updateById(activity);
        // 然后 删除redis
        return updateById;
    }


    // 这里就是 异步线程  创建一个线程池
    private final static ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
    // 这里我们还是 预先预热  然后到redis当中进行查询实现逻辑过期 防止 缓存击穿
    @Override
    public Activity selectById(String id) {
        String key = ACTIVITY_KEY + id;
        String activityStr = stringRedisTemplate.opsForValue().get(key);

        // 如果 redis当中 根本就没有 那么 直接返回null
        if(StrUtil.isBlank(activityStr)){
            return null;
        }

        RedisData redisData = JSONUtil.toBean(activityStr, RedisData.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        JSONObject activityJson = (JSONObject) redisData.getData();
        Activity activity = JSONUtil.toBean(activityJson, Activity.class);

        if(expireTime.isAfter(LocalDateTime.now())){
            // 没有过期  那么 我们可以直接返回
            return activity;
        }

        // 信息过期了  那么我们需要 开启另外一个线程进行更新redis当中的数据
        String lock_key = LOCK_ACTIVITY_KEY + id;
        // 如果 获取 分布式锁成功  那么 就可以进行更新
        if(tryLock(lock_key)){
            CACHE_REBUILD_EXECUTOR.submit(()->{
                try{
                    saveActivity2Redis(id,20L);
                }catch (Exception e){
                    throw new RuntimeException(e);
                }finally {
                    unLock(lock_key);
                }

            });

            // 这里还是 需要到redis当中查看  获取锁之后 是否真的过期了
            // 如果没有过期  那么 我们就直接返回
            String result = stringRedisTemplate.opsForValue().get(key);
            RedisData redisData1 = JSONUtil.toBean(result, RedisData.class);
            LocalDateTime expireTime1 = redisData1.getExpireTime();
            JSONObject data = (JSONObject) redisData1.getData();
            Activity activity1 = JSONUtil.toBean(data, Activity.class);

            if(expireTime1.isAfter(LocalDateTime.now())){
                return activity1;
            }

        }
        // 返回过期信息
        return activity;
    }

    public void saveActivity2Redis(String id,Long expireSeconds){

        String key = ACTIVITY_KEY + id;
        Activity activity = getById(id);
        RedisData redisData = new RedisData();
        redisData.setData(activity);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));

        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));

    }
    public boolean tryLock(String key){
        Boolean isSuccess = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.MINUTES);
        return BooleanUtil.isTrue(isSuccess);
    }

    public void unLock(String key){
        stringRedisTemplate.delete(key);
    }
    @Override
    @Transactional
    public List<Activity> selectByIds(List<String> ids) {
        List<Activity> activities = activityDao.selectBatchIds(ids);
        return activities;
    }

    @Override
    @Transactional
    public List<Activity> selectAll() {
        List<Activity> list = list();
        for (Activity activity : list) {
            String key = ACTIVITY_KEY + activity.getId();
            RedisData redisData = new RedisData();
            redisData.setExpireTime(LocalDateTime.now().plusSeconds(20L));
            redisData.setData(activity);

            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));
        }
        return list;
    }

    @Override
    public int DownLoad(Activity activity) {
        int updateById = activityDao.updateById(activity);
        return updateById;
    }

    @Override
    public List<Activity> selectByName(String name) {
        LambdaQueryWrapper<Activity> lqw  = new LambdaQueryWrapper<>();
        lqw.eq(Activity::getKind,name);
        List<Activity> activities = activityDao.selectList(lqw);
        return activities;
    }

    @Override
    public List<Activity> getAllByCommunityName(String name) {
        List<Activity> allByCommunityName = activityDao.getAllByCommunityName(name);
        return allByCommunityName;
    }

    @Override
    public String getCommunityByName(String name) {
        String community = activityDao.getCommunityByName(name);
        return community;
    }

    @Override
    public List<Activity> getBySchool(String school) {
        List<Activity> schools = activityDao.getBySchool(school);
        return schools;
    }

    @Override
    public List<Activity> getByLikeclick() {
        List<Activity> byLikeclick = activityDao.getByLikeclick();
        return byLikeclick;
    }

    @Override
    public Result updateCommunity(Activity activity) {
        // 先更新数据库
        boolean isSuccess = updateById(activity);
        // 然后删除redis
        stringRedisTemplate.delete(ACTIVITY_KEY + activity.getId());
        String mes = isSuccess ? "更新成功" : "更新失败";
        Integer code = isSuccess ? 200 : 500;
        return new Result(code,null,mes);
    }
}
